%%
%% Entry point for gossip_monitor process, passed all gossip_nodes information as well as the PID of main process.
%% Statistics information is initialized. The format is  {{ActiveSend, ActiveReceiveNormal, ActiveReceiveReject, ActiveReceiveTimeout}, {PassiveSendNormal, PassiveSendReject}}.
%% The process will go to the main loop immediately.
%%
%% Parameters:
%% 	Nodes: Array of {ID, PID} for all the gossip nodes
%% 	Main: PID of the main process
%%
init(Nodes, Main) ->
	GlobalMap = [{ID, -1, {{0, 0, 0, 0}, {0, 0}}} || {ID, _Pid} <- Nodes],
	loop(Nodes, GlobalMap, Main).

%%
%% Main loop for the gossip_monitor, mainly for receiving the statistics message as well as the report of value change.
%% When value change is reported, a checking of all the nodes value is done, if the values are already balanced,
%% "stop" message will be send to all the "gossip_node"s, and this monitor will also exit after a final statistics report
%% to the main process.
%%
%% Different types of received messages are handled:
%%	"report" message: update GlobalMap, and check if the data is ready
%%	"stat" message: update GlobalMap, the statistics part
%% 
%% Parameters:
%% 	Nodes: Array of {ID, PID} for all the gossip nodes
%%	GlobalMap: Array of {ID, CPUConfig, Statistics} for all the gossip nodes
%% 	Main: PID of the main process
%%
loop(Nodes, GlobalMap, Main) ->
	receive
		{report, NID, Value} ->
			GlobalMap1 = update_map(GlobalMap, NID, Value),
			EqualFlag = check_equal(GlobalMap1),
			if
				EqualFlag == true->
					[Pid ! {stop} || {_ID, Pid} <- Nodes],
					Main ! {finish, GlobalMap1};
				true ->
					loop(Nodes, GlobalMap1, Main)
			end;
		
		{stat, NID, Stat} ->
			GlobalMap2 = update_stat(GlobalMap, NID, Stat),
			loop(Nodes, GlobalMap2, Main)
	end.

update_map(GlobalMap, NID, NValue) ->
	[ if
		  ID == NID ->
			  {ID, NValue, _Stat};
		  true ->
			  {ID, Value, _Stat}
	  end || {ID, Value, _Stat} <- GlobalMap].

update_stat(GlobalMap, NID, Stat) ->
	{{I1, I2, I3, I4}, {I5, I6}} = Stat,
	[ if
		  ID == NID ->
			  {ID, Value, {{S1+I1, S2+I2, S3+I3, S4+I4}, {S5+I5, S6+I6}}};
		  true ->
			  {ID, Value, {{S1, S2, S3, S4}, {S5, S6}}}
	  end || {ID, Value, {{S1, S2, S3, S4}, {S5, S6}}} <- GlobalMap].

check_equal(GlobalMap) ->
	GlobalMapValue = [Value || {_ID, Value, _Stat} <- GlobalMap],
	Min = lists:min(GlobalMapValue),
	Max = lists:max(GlobalMapValue),
	Abs = erlang:abs(Min-Max),
	if
		(Abs < 0.99999999999999999) and (Min /= -1) ->
			true;
		true ->
			false
	end.
